#include "morph.h"
#include <osgDB/ReadFile>
#include <iostream>
#include <osg/Vec3d>
#include <osg/StateSet>
#include <osg/BlendColor>
#include <osg/BlendFunc>
#include <osg/NodeVisitor>

//----------------------------------------------------------------------
class FindGeodeVisitor : public osg::NodeVisitor
//----------------------------------------------------------------------
{
public:
    FindGeodeVisitor(std::string geode_name = ""):
        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
        {name = geode_name;};
    virtual void apply(osg::Geode& g)
    {
        if( name == "" || g.getName() == name )
          foundList.push_back(&g);
        traverse(g);
    }
    typedef std::vector< osg::ref_ptr<osg::Geode> > NodeList;
    NodeList foundList;
    std::string name;
};
//----------------------------------------------------------------------
Morph::Morph(void) : Node()
//----------------------------------------------------------------------
{
  _nv=0;
}
//----------------------------------------------------------------------
Morph::Morph(Node* parent) : Node(parent)
//----------------------------------------------------------------------
{
  _nv=0;
  _weights.push_back(0);
  _weights.push_back(0);
  _weights.push_back(0);
}
//----------------------------------------------------------------------
Morph::~Morph(void)
//----------------------------------------------------------------------
{
}
//----------------------------------------------------------------------
bool Morph::Load(std::string filename, std::string geode_name )
//----------------------------------------------------------------------
{
  if( ! Node::Load(filename) ) return false;
  
  osg::Vec3Array *v = FindVertexes(_node.get(),geode_name);  
  if( !v )
  {
    std::cout << "DemoEngine::Morph : No Geometry found" << std::endl;
    return false;
  }
  v->setDataVariance(osg::Object::DYNAMIC);
  _nv = v->size();
  _node_vert = v;

  _base_vert = new osg::Vec3Array();
  for(int i=0; i<_nv; i++) _base_vert->push_back( (*_node_vert)[i] );

  // TODO: kill the previous targets
  return true;  
}
//----------------------------------------------------------------------
bool Morph::AddTarget(std::string filename, std::string geode_name )
//----------------------------------------------------------------------
{
  //if(_targets.size() >= 4) 
  //{
  //  std::cout << "DemoEngine::Morph : Too many Targets" << std::endl;
  //  return false;
  //}
  osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(filename);
  if (!node) 
  {
    std::cout << "DemoEngine::Morph : No data loaded" << std::endl;
    return false;
  }
  osg::Vec3Array *v = Morph::FindVertexes(node.get(),geode_name);  
  if( !v )
  {
    std::cout << "DemoEngine::Morph : No Geometry found in Morph Target" << std::endl;
    return false;
  }
  if( _nv != v->size())
  {
    std::cout << "DemoEngine::Morph : wrong number of vertexes in Morph Target" << std::endl;
    return false;
  }
  
  osg::Vec3Array *t = new osg::Vec3Array();
  for(int i=0; i<_nv; i++) t->push_back( (*v)[i]-(*_base_vert)[i] );

  _targets.push_back( t );
  if(_weights.size() < _targets.size() ) _weights.push_back(0);

  return true;  
}
//----------------------------------------------------------------------
osg::Vec3Array *Morph::FindVertexes(osg::Node* n, std::string geode_name )
//----------------------------------------------------------------------
{
  //osg::ref_ptr<FindGeodeVisitor> fg = new FindGeodeVisitor(); // provare a togliere il ref_ptr
  FindGeodeVisitor fg(geode_name);
  n->accept(fg);
  if(fg.foundList.size() == 0) return NULL;
  osg::Geode *g = fg.foundList[0].get();
  if(g->getNumDrawables() == 0) return NULL;
  osg::Geometry *geom = g->getDrawable(0)->asGeometry();
  if(!geom) return NULL;
  geom->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
  geom->setUseVertexBufferObjects(false);
  geom->setSupportsDisplayList(false);

  osg::Vec3Array *v= dynamic_cast<osg::Vec3Array*>( geom->getVertexArray() );
  return v;
}
//----------------------------------------------------------------------
void Morph::SetTargetWeights(Vec v)
//----------------------------------------------------------------------
{
  double min=-2; double max=2;
  _weights[0]= osg::clampBetween( v.x,     min, max );
  _weights[1]= osg::clampBetween( v.y,     min, max );
  _weights[2]= osg::clampBetween( v.z,     min, max );
  Update();
}
//----------------------------------------------------------------------
void Morph::Update()
//----------------------------------------------------------------------
{
  osg::Vec3Array *base = _base_vert.get();
  osg::Vec3Array *dest = _node_vert.get();

  for( int i=0; i<_nv; i++)
  {
    osg::Vec3f& dst = (*dest)[i];
    osg::Vec3f  src = (*base)[i];
    dst.set(src);
  }

  for( size_t t=0; t<_targets.size(); t++)
  {
    osg::Vec3Array *tar = _targets[t].get();      
    float w = _weights[t];
    if(w != 0)
    {
      for( int i=0; i<_nv; i++)
      {
        osg::Vec3f& dst = (*dest)[i];
        osg::Vec3f  src = (*tar)[i];
        dst.set( dst + src*w );
      }
    }
  }
  _node->dirtyBound();
}
